/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.loaders; import java.beans.*; import java.awt.event.*; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.ResourceBundle; import javax.swing.JDialog; import org.openide.*; import org.openide.compiler.*; import org.openide.compiler.Compiler; import org.openide.debugger.*; import org.openide.execution.*; import org.openide.explorer.propertysheet.*; import org.openide.filesystems.*; import org.openide.cookies.CompilerCookie; import org.openide.cookies.ExecCookie; import org.openide.cookies.ArgumentsCookie; import org.openide.cookies.DebuggerCookie; import org.openide.nodes.Sheet; import org.openide.nodes.PropertySupport; import org.openide.util.NbBundle; import org.openide.util.Mutex; import org.openide.util.Utilities; /** Support for execution of a class file. Looks for the class with * the same base name as the primary file, locates a main method * in it, and starts it. * * @author Jaroslav Tulach */ public class ExecSupport extends Object implements ExecCookie, ArgumentsCookie, DebuggerCookie { /** extended attribute for the type of executor */ private static final String EA_EXECUTOR = "NetBeansAttrExecutor"; // NOI18N /** extended attribute for attributes */ private static final String EA_ARGUMENTS = "NetBeansAttrArguments"; // NOI18N /** extended attribute for debugger type */ private static final String EA_DEBUGGER_TYPE = "NetBeansAttrDebuggerType"; // NOI18N // copy from JavaNode /** Name of property providing argument parameter list. */ public static final String PROP_FILE_PARAMS = "params"; // NOI18N /** Name of property providing a custom {@link Executor} for a file. */ public static final String PROP_EXECUTION = "execution"; // NOI18N /** Name of property providing a custom {@link DebuggerType} for a file. */ public static final String PROP_DEBUGGER_TYPE = "debuggerType"; // NOI18N /** bundle */ static ResourceBundle bundle; /** entry to be associated with */ protected MultiDataObject.Entry entry; /** Create new support for given entry. The file is taken from the * entry and is updated if the entry moves or renames itself. * @param entry entry to create instance from */ public ExecSupport (MultiDataObject.Entry entry) { this.entry = entry; } /* Starts the class. */ public void start () { Executor exec = getExecutor (entry); if (exec == null) { exec = defaultExecutor (); } String[] params = getArguments (); try { exec.execute(new ExecInfo(entry.getFile().getPackageName ('.'), params)); } catch (final IOException ex) { Mutex.EVENT.readAccess (new Runnable () { public void run () { if (startFailed (ex)) { // restart ExecSupport.this.start (); } } }); } } /* Start debugging of associated object. * @param stopOnMain if <code>true</code>, debugger stops on the first line of debugged code * @exception DebuggerException if the session cannot be started */ public void debug (final boolean stopOnMain) throws DebuggerException { String[] params = getArguments (); DebuggerType t = getDebuggerType (entry); if (t == null) { t = defaultDebuggerType (); } try { ExecInfo ei = new ExecInfo (entry.getFile ().getPackageName ('.'), params); t.startDebugger (ei, stopOnMain); // ok, debugger started return; } catch (final DebuggerException ex) { try { Mutex.EVENT.readAccess (new Mutex.ExceptionAction () { public Object run () throws DebuggerException { if (debugFailed (ex)) { // restart debug (stopOnMain); } return null; } }); } catch (org.openide.util.MutexException mx) { throw (DebuggerException)mx.getException (); } } } /** Called when invocation of the executor fails. Allows to do some * modifications to the type of execution and try it again. * * @param ex exeception that occured during execution * @return true if the execution should be restarted */ protected boolean startFailed (IOException ex) { Executor e = (Executor)choose (getExecutor (entry), Executor.class, ex); if (e == null) { return false; } else { try { setExecutor (entry, e); return true; } catch (IOException exc) { return false; } } } /** Called when invocation of the debugger fails. Allows to do some * modifications to the type of debugging and try it again. * * @param ex exeception that occured during execution * @return true if the debugging should be started again */ protected boolean debugFailed (DebuggerException ex) { DebuggerType e = (DebuggerType)choose (getDebuggerType (entry), DebuggerType.class, ex); if (e == null) { return false; } else { try { setDebuggerType (entry, e); return true; } catch (IOException exc) { return false; } } } /** Check if this object is up to date or in need of compilation. * Should compile it if necessary. * <p>The default implementation checks whether {@link CompilerCookie} is provided and * if so, creates a job and compiles the object. This behavior may be * overridden by subclasses. * * @return <code>true</code> if the object was successfully brought up to date, <code>false</code> if the attempt failed (and it may be still be out of date) * @deprecated The check should be done in an action - ExecAction, ... */ protected boolean checkCompiled () { DataObject obj = entry.getDataObject (); CompilerCookie c = (CompilerCookie)obj.getCookie (CompilerCookie.class); if (c != null) { CompilerJob job = new CompilerJob (Compiler.DEPTH_ZERO); job.setDisplayName (obj.getName ()); c.addToJob (job, Compiler.DEPTH_ZERO); if (!job.isUpToDate ()) { // add name // compile it CompilerTask t = job.start (); return t.isSuccessful (); } } return true; } /** This method allows subclasses to override the default * debugger type they want to use for debugging. * * @return current implementation returns DebuggerType.getDefault () */ protected DebuggerType defaultDebuggerType () { return DebuggerType.getDefault (); } /** This method allows subclasses to override the default * executor they want to use for debugging. * * @return current implementation returns Executor.getDefault () */ protected Executor defaultExecutor () { return Executor.getDefault (); } /** Set the executor for a given file object. * Uses file attributes to store this information. * @param entry entry to set the executor for * @param exec executor to use * @exception IOException if executor cannot be set */ public static void setExecutor (MultiDataObject.Entry entry, Executor exec) throws IOException { entry.getFile ().setAttribute (EA_EXECUTOR, exec == null ? null : new Executor.Handle (exec) ); } /** Get the executor for a given file object. * @param entry entry to obtain the executor for * @return executor associated with the file, or null if the default should be used */ public static Executor getExecutor (MultiDataObject.Entry entry) { try { Executor.Handle handle = (Executor.Handle)entry.getFile ().getAttribute (EA_EXECUTOR); if (handle != null) { ServiceType exec = handle.getServiceType (); if (exec instanceof Executor) { return (Executor)exec; } } } catch (Exception ex) { // IOException } return null; } /* Sets execution arguments for the associated entry. * @param args array of arguments * @exception IOException if arguments cannot be set */ public void setArguments (String[] args) throws IOException { entry.getFile ().setAttribute (EA_ARGUMENTS, args); } /** Set execution arguments for a given entry. * @param entry the entry * @param args array of arguments * @exception IOException if arguments cannot be set */ public static void setArguments (MultiDataObject.Entry entry, String[] args) throws IOException { entry.getFile ().setAttribute (EA_ARGUMENTS, args); } /* Getter for arguments associated with given file. * @return the arguments or empty array if no arguments associated */ public String[] getArguments () { try { String[] args = (String[])entry.getFile ().getAttribute (EA_ARGUMENTS); if (args != null) { return args; } } catch (Exception ex) { // null pointer or IOException } return new String[0]; } /** Get the arguments associated with a given entry. * @param entry the entry * @return the arguments, or an empty array if no arguments are specified */ public static String[] getArguments(MultiDataObject.Entry entry) { try { String[] args = (String[])entry.getFile ().getAttribute (EA_ARGUMENTS); if (args != null) { return args; } } catch (Exception ex) { // null pointer or IOException } return new String[0]; } // // debugger support // /** Assignes a debugger type to an entry. * @param entry the object's entry * @param type the debugger type for this entry * @exception IOException if arguments cannot be set */ public static void setDebuggerType (MultiDataObject.Entry entry, DebuggerType type) throws IOException { entry.getFile ().setAttribute (EA_DEBUGGER_TYPE, type == null ? null : new DebuggerType.Handle (type) ); } /** Retrieves the debugger type for this entry. * @param entry the entry * @return the debugger type or null if no type assigned */ public static DebuggerType getDebuggerType (MultiDataObject.Entry entry) { try { DebuggerType.Handle h = (DebuggerType.Handle)entry.getFile ().getAttribute (EA_DEBUGGER_TYPE); ServiceType t = h.getServiceType (); if (t instanceof DebuggerType) { return (DebuggerType)t; } } catch (Exception ex) { } return null; } /** Helper method that creates default properties for execution of * a given support. * Includes properties to set the executor; debugger; and arguments. * * @param set sheet set to add properties to */ public void addProperties (Sheet.Set set) { set.put(new PropertySupport.ReadWrite ( PROP_FILE_PARAMS, String.class, getString("PROP_fileParams"), getString("HINT_fileParams") ) { public Object getValue() { String[] args = getArguments (); StringBuffer b = new StringBuffer(50); for (int i = 0; i < args.length; i++) { b.append(args[i]).append(' '); } return b.toString(); } public void setValue (Object val) throws InvocationTargetException { if (val instanceof String) { try { setArguments(Utilities.parseParameters((String)val)); } catch(IOException e) { throw new InvocationTargetException (e); } } else { throw new IllegalArgumentException(); } } public boolean supportsDefaultValue () { return true; } public void restoreDefaultValue () throws InvocationTargetException { try { setArguments(null); } catch(IOException e) { throw new InvocationTargetException (e); } } } ); set.put(createExecutorProperty ()); set.put(createDebuggerProperty ()); } /** Creates the executor property for entry. * @return the property */ private PropertySupport createExecutorProperty () { return new PropertySupport.ReadWrite ( PROP_EXECUTION, Executor.class, getString("PROP_execution"), getString("HINT_execution") ) { public Object getValue() { Executor e = getExecutor (entry); if (e == null) return defaultExecutor (); else return e; } public void setValue (Object val) throws InvocationTargetException { try { setExecutor(entry, (Executor) val); } catch (IOException ex) { throw new InvocationTargetException (ex); } } public boolean supportsDefaultValue () { return true; } public void restoreDefaultValue () throws InvocationTargetException { setValue (null); } }; } /** Creates the debugger property for entry. * @return the property */ private PropertySupport createDebuggerProperty () { return new PropertySupport.ReadWrite ( PROP_DEBUGGER_TYPE, DebuggerType.class, getString("PROP_debuggerType"), getString("HINT_debuggerType") ) { public Object getValue() { DebuggerType dt = getDebuggerType (entry); if (dt == null) return defaultDebuggerType (); else return dt; } public void setValue (Object val) throws InvocationTargetException { try { setDebuggerType (entry, (DebuggerType) val); } catch (IOException ex) { throw new InvocationTargetException (ex); } } public boolean supportsDefaultValue () { return true; } public void restoreDefaultValue () throws InvocationTargetException { setValue (null); } }; } /** @return a localized String */ static String getString(String s) { if (bundle == null) { bundle = NbBundle.getBundle(ExecSupport.class); } return bundle.getString(s); } /** Opens dialog and asks for different executor or debugger. * @param current current value * @param clazz the class to use to locate property editor * @param ex the exception that caused the problem * @return the new value or null if choosing failed */ private static ServiceType choose (ServiceType current, Class clazz, final Exception ex) { PropertyEditor ed = PropertyEditorManager.findEditor (clazz); if (ed == null) return null; ed.setValue (current); java.awt.Component c = ed.getCustomEditor (); if (c == null) return null; String configure = org.openide.util.NbBundle.getBundle(ExecSupport.class).getString("CTL_ServiceConfigure"); NotifyDescriptor exc = new NotifyDescriptor.Exception (ex); exc.setTitle (org.openide.util.NbBundle.getBundle(ExecSupport.class).getString("CTL_Service_Configuration_Title")); exc.setMessage (ex.getLocalizedMessage ()); exc.setOptions (new Object[] { configure, NotifyDescriptor.CANCEL_OPTION }); Object res = TopManager.getDefault ().notify (exc); if (!configure.equals (res)) { return null; } DialogDescriptor d = new DialogDescriptor ( c, getString ("MSG_ConfigureService") ); d.setOptions (new Object[] { DialogDescriptor.OK_OPTION, DialogDescriptor.CANCEL_OPTION }); java.awt.Dialog dialog = TopManager.getDefault ().createDialog (d); dialog.show (); if (d.getValue () == NotifyDescriptor.OK_OPTION) { // get the current value return (ServiceType)ed.getValue (); } else { // canceled return null; } } } /* * Log * 33 Gandalf 1.32 1/12/00 Ian Formanek NOI18N * 32 Gandalf 1.31 1/7/00 Jaroslav Tulach #5111 * 31 Gandalf 1.30 1/5/00 Jaroslav Tulach Service configuration has * two dialogs * 30 Gandalf 1.29 11/24/99 Ales Novak thread that waited for * the end of a running process moved to ProcessExecutor * 29 Gandalf 1.28 10/29/99 Jesse Glick Removed deprecated static * variants of {Exec,Compiler}Support.addProperties. * 28 Gandalf 1.27 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 27 Gandalf 1.26 10/7/99 Jesse Glick Encouraged to use * nonstatic methods to add properties to sheet sets for supports. * 26 Gandalf 1.25 10/1/99 Jesse Glick Cleanup of service type * name presentation. * 25 Gandalf 1.24 10/1/99 Ales Novak major change of execution * 24 Gandalf 1.23 9/15/99 Jaroslav Tulach Query when wrong executor * or debugger is used. * 23 Gandalf 1.22 9/10/99 Jaroslav Tulach Changes in services APIs. * 22 Gandalf 1.21 7/12/99 Martin Ryzl access modifier for entry * changed to protected * 21 Gandalf 1.20 6/28/99 Jaroslav Tulach Debugger types are like * Executors * 20 Gandalf 1.19 6/11/99 Jan Jancura * 19 Gandalf 1.18 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 18 Gandalf 1.17 6/4/99 Petr Jiricka * 17 Gandalf 1.16 5/14/99 Ales Novak bugfix for #1667 #1598 * #1625 * 16 Gandalf 1.15 4/21/99 Jaroslav Tulach Debugger types. * 15 Gandalf 1.14 4/21/99 Ales Novak NullPointerEx removed * 14 Gandalf 1.13 4/2/99 Jesse Glick [JavaDoc] * 13 Gandalf 1.12 4/2/99 Jaroslav Tulach Compiles before * execution. * 12 Gandalf 1.11 3/22/99 Jesse Glick [JavaDoc] * 11 Gandalf 1.10 3/19/99 Ales Novak * 10 Gandalf 1.9 3/19/99 Ales Novak * 9 Gandalf 1.8 3/14/99 Jaroslav Tulach Change of * MultiDataObject.Entry. * 8 Gandalf 1.7 3/11/99 Jesse Glick [JavaDoc] * 7 Gandalf 1.6 3/11/99 Jan Jancura * 6 Gandalf 1.5 3/9/99 Jesse Glick [JavaDoc] * 5 Gandalf 1.4 3/3/99 Jaroslav Tulach Also implements debugger * cookie. * 4 Gandalf 1.3 2/4/99 Petr Hamernik setting of extended file * attributes doesn't require FileLock * 3 Gandalf 1.2 1/20/99 David Simonek rework of class DO * 2 Gandalf 1.1 1/6/99 Ian Formanek * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ */